package com.daily.DailyStudy.redis.分布式锁;


import redis.clients.jedis.Jedis;

public class Redis_01 {

    public boolean lock(Jedis jedis,String lockName,Integer expire){
        //返回是否设置成功
        //setNx加锁
        long now = System.currentTimeMillis();
        boolean result = jedis.setnx(lockName,String.valueOf(now+expire*1000))==1;
        if(!result){
            //防止死锁的容错
            String timestamp = jedis.get(lockName);
            if (timestamp != null && Long.parseLong(timestamp) < now) {
                //不通过del方法来删除锁。而是通过同步的getSet
                String oldValue = jedis.getSet(lockName, String.valueOf(now + expire));
                if (oldValue != null && oldValue.equals(timestamp)) {
                    result = true;
                    jedis.expire(lockName, expire);
                }
            }
        }
        if (result){
            jedis.expire(lockName,expire);
        }
        return result;
    }
    /**
     * 代码分析：
     *
     * 通过setNx命令老保证操作的原子性，获取到锁，并且把过期时间设置到value里面
     *
     * 通过expire方法设置过期时间，如果设置过期时间失败的话，再通过value的时间戳来和当前时间戳比较，防止出现死锁
     *
     * 通过getSet命令在发现锁过期未被释放的情况下，避免删除了在这个过程中有可能被其余的线程获取到了锁
     *
     * 存在问题
     *
     * 防止死锁的解决方案是通过系统当前时间决定的，不过线上服务器系统时间一般来说都是一致的，这个不算是严重的问题
     *
     * 锁过期的时候可能会有多个线程执行getSet命令，在竞争的情况下，会修改value的时间戳，理论上来说会有误差
     *
     * 锁无法具备客户端标识，在解锁的时候可能被其余的客户端删除同一个key
     *
     * 虽然有小问题，不过大体上来说这种分布式锁的实现方案基本上是符合要求的，能够做到锁的互斥和避免死锁
     *
     * 作者：Java黎先生
     * 链接：https://www.jianshu.com/p/bdcc535183ea
     * 来源：简书
     *
     * */
}
